package org.oos.model;

import static org.oos.dao.options.OptionsMap.FINDERS.CHECKIN_OPTIONS;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.oos.OOS4Java;
import org.oos.atom.elements.Author;
import org.oos.atom.elements.Text;
import org.oos.atom.entries.CategoryEntry;
import org.oos.atom.entries.ContactEntry;
import org.oos.atom.entries.ReviewEntry;
import org.oos.atom.entries.UserEntry;
import org.oos.atom.entries.UserEntryImpl;
import org.oos.auth.UserPrivacyType;
import org.oos.dao.ContactDao;
import org.oos.dao.SocialActivityDao;
import org.oos.dao.UserActivityDao;
import org.oos.dao.UserCheckinDao;
import org.oos.dao.UserDao;
import org.oos.dao.UserListsDao;
import org.oos.dao.UserSiteDao;
import org.oos.dao.UserTagsDao;
import org.oos.dao.exceptions.CreationException;
import org.oos.dao.exceptions.ReadException;
import org.oos.dao.exceptions.UpdateException;
import org.oos.dao.options.CheckinOptions;
import org.oos.lists.ActivityList;
import org.oos.lists.ContactList;
import org.oos.lists.DynamicList;
import org.oos.lists.ReviewList;
import org.oos.util.TextUtils;

public class User extends AbstractEntityImpl implements Author {
	private static final long serialVersionUID = 1724594623693662947L;

	private UserEntry entry;
	private ReviewList reviews;
	private ContactList contacts;
	private ActivityList activity;
	private ActivityList socialActivity;
	private List<CharSequence> lists;
	private List<CharSequence> tags;

	private long lastActivityTime;
	private final static int secondsRefreshActivity = 5 * 60;

	public User(OOS4Java oos4Java, UserEntry entry) {
		super(oos4Java);
		this.entry = entry;
	}

	public User(OOS4Java oos4Java, Author author) {
		super(oos4Java);
		this.entry = new UserEntryImpl(author);
	}

	public UserEntry getEntry() {
		return entry;
	}

	public void setEntry(UserEntry entry) {
		this.entry = entry;
	}

	@Override
	public UserAvatar getAvatar() {
		return entry.getAvatar();
	}

	@Override
	public String getSlug() {
		return entry.getSlug();
	}

	@Override
	public String getNick() {
		return entry.getNick();
	}

	public void setNick(String nick) {
		entry.setNick(nick);
	}

	public String getName() {
		return entry.getName();
	}

	public void setName(String name) {
		entry.setName(name);
	}

	public String getSurname() {
		return entry.getSurname();
	}

	public void setSurname(String surname) {
		entry.setSurname(surname);
	}

	public Text getAboutMe() {
		return entry.getAboutMe();
	}

	public void setAboutMe(Text about) {
		entry.setAboutMe(about);
	}

	public String getMail() {
		return entry.getMail();
	}

	public void setMail(String mail) {
		entry.setMail(mail);
	}

	public String getTelephone() {
		return entry.getTelephone();
	}

	public void setTelephone(String phone) {
		entry.setTelephone(phone);
	}

	public Boolean getNomails() {
		return entry.getNomails();
	}

	public void setNomails(Boolean bool) {
		entry.setNomails(bool);
	}

	public Boolean getNonewsletter() {
		return entry.getNonewsletter();
	}

	public void setNonewsletter(Boolean bool) {
		entry.setNonewsletter(bool);
	}

	public Boolean getOnlycontacts() {
		return entry.getOnlycontacts();
	}

	public void setOnlycontacts(Boolean bool) {
		entry.setOnlycontacts(bool);
	}

	public Boolean getNobulletin() {
		return entry.getNobulletin();
	}

	public void setNobulletin(Boolean bool) {
		entry.setNobulletin(bool);
	}

	public Boolean getNomailonmediacomments() {
		return entry.getNomailonmediacomments();
	}

	public void setNomailonemdiacomments(Boolean bool) {
		entry.setNomailonmediacomments(bool);
	}

	public Boolean getNomailonlikes() {
		return entry.getNomailonlikes();
	}

	public void setNomailonlikes(Boolean bool) {
		entry.setNomailonlikes(bool);
	}

	public Boolean getNomailoncommentsonreview() {
		return entry.getNomailoncommentsonreview();
	}

	public void setNomailoncommentsonreview(Boolean bool) {
		entry.setNomailoncommentsonreview(bool);
	}

	public Boolean getNosuggestedcontacts() {
		return entry.getNosuggestedcontacts();
	}

	public void setNosuggestedcontacts(Boolean bool) {
		entry.setNosuggestedcontacts(bool);
	}

	public Boolean isTwitterConnected() {
		return entry.getTwitter();
	}

	public Boolean isFacebookConnected() {
		return entry.getFacebook();
	}

	public void setFacebookConnected(boolean connected) {
		entry.setFacebook(connected);
	}

	public void setTwitterConnected(boolean connected) {
		entry.setTwitter(connected);
	}

	public Date getUpdated() {
		return entry.getUpdated();
	}

	public void setUpdated(Date updated) {
		entry.setUpdated(updated);
	}

	@Override
	public String toString() {
		return getNick();
	}

	public void update() throws UpdateException {
		UserDao dao = oos4Java.getUserDao();
		dao.update(entry);
	}

	public UserAvatar updateAvatar(File file, String contentType)
			throws UpdateException, ReadException {
		UserDao dao = oos4Java.getUserDao();
		dao.updateMedia(entry, file, contentType);
		oos4Java.resetMe();
		return oos4Java.getMe().getAvatar();
	}

	public boolean hasSaved(Site site) throws ReadException {
		if (site.getOosId() == null) {
			return false;
		}

		if (reviews != null) {
			for (Review review : reviews) {
				if (review.getSite().getSlug().equals(site.getSlug())) {
					return true;
				}
			}
			return false;
		} else {
			return getUserSiteDao().readEntry(site.getSlug()) != null;
		}
	}

	public boolean hasSaved(User user) throws ReadException {
		if (user.equals(this)) {
			return true;
		}

		if (contacts != null) {
			for (Contact contact : contacts) {
				if (contact.getContactSlug().equals(user.getSlug())) {
					return true;
				}
			}
			return false;
		} else {
			return getContactDao().readEntry(user.getSlug()) != null;
		}
	}

	private UserSiteDao getUserSiteDao() {
		UserSiteDao dao = oos4Java.getUserSiteDao();
		dao.setUserSlug(getSlug());
		return dao;
	}

	private ContactDao getContactDao() {
		ContactDao dao = oos4Java.getContactDao();
		dao.setUserSlug(getSlug());
		return dao;
	}

	public ReviewList getReviews() throws ReadException {
		if (reviews == null) {
			reviews = new ReviewList(oos4Java, getUserSiteDao().read());
		}

		return reviews;
	}

	public ContactList getContacts() throws ReadException {
		if (contacts == null) {
			contacts = new ContactList(oos4Java, getContactDao().read());
		}

		return contacts;
	}

	private boolean activityTooOld() {
		long now = Calendar.getInstance().getTimeInMillis();
		return (lastActivityTime != 0)
				&& (now - lastActivityTime > 1000 * secondsRefreshActivity);
	}

	public ActivityList getActivity() throws ReadException {
		if (activity == null || activityTooOld()) {
			UserActivityDao dao = oos4Java.getUserActivityDao();
			dao.setUserSlug(getSlug());
			activity = new ActivityList(oos4Java, dao.read());
			lastActivityTime = Calendar.getInstance().getTimeInMillis();
		}

		return activity;
	}

	public ActivityList getSocialActivity() throws ReadException {
		if (socialActivity == null || activityTooOld()) {
			SocialActivityDao dao = oos4Java.getSocialActivityDao();
			dao.setUserSlug(getSlug());
			socialActivity = new ActivityList(oos4Java, dao.read());
			lastActivityTime = Calendar.getInstance().getTimeInMillis();
		}

		return socialActivity;
	}

	// TODO: getCheckins

	public List<CharSequence> getLists() throws ReadException {
		if (lists == null) {
			UserListsDao dao = oos4Java.getUserListsDao();
			dao.setUserSlug(getSlug());
			lists = new ArrayList<CharSequence>();
			for (CategoryEntry category : dao.read()) {
				lists.add(category.getTerm());
			}
		}

		return lists;
	}

	public void addList(String list) {
		lists.add(list);
	}

	public List<CharSequence> getTags() throws ReadException {
		if (tags == null) {
			UserTagsDao dao = oos4Java.getUserTagsDao();
			dao.setUserSlug(getSlug());
			tags = new ArrayList<CharSequence>();
			for (CategoryEntry category : dao.read()) {
				tags.add(category.getTerm());
			}
		}

		return tags;
	}

	public void addTag(String tag) {
		tags.add(tag);
	}

	public String makeCheckin(Site site, float longitude, float latitude,
			String comment) {
		UserCheckinDao dao = oos4Java.getUserCheckinDao();

		dao.setUserSlug(this.getSlug());
		CheckinOptions options = CHECKIN_OPTIONS.get(dao.getOptions());
		options.setSiteSlug(site.getSlug());
		options.setLatitude(latitude);
		options.setLongitude(longitude);
		try {
			comment = URLEncoder.encode(comment, "UTF-8");
		} catch (UnsupportedEncodingException e) {
		}
		options.setComment(comment);

		try {
			if (dao.create() != null) {
				return null;
			}
		} catch (CreationException e) {
			try {
				return TextUtils.convertStreamToString(e.getResponse()
						.getContent());
			} catch (Exception e1) {
			}
		}
		return "Problema desconocido";
	}

	public void addContact(Contact contact) throws ReadException {
		getContacts().add(contact);
	}

	public void addContact(User user, String nick, boolean trusted)
			throws ReadException {
		Contact contact = new Contact(oos4Java, this, user);
		contact.setContactNick(nick);
		contact.setTrusted(trusted);
		this.addContact(contact);
	}

	public boolean addReview(Review review) throws ReadException {
		return getReviews().add(review);
	}

	public boolean addReview(Site site, String title, String content,
			Integer rating, UserPrivacyType privacy, List<String> tags,
			List<String> lists) throws ReadException {
		if (!this.hasSaved(site)) {
			Review review = new Review(oos4Java, this, site);
			review.setTitle(title);
			review.setContent(content);
			review.setRatingValue(rating);
			review.setPrivacy(privacy);
			review.setTags(tags);
			review.setLists(lists);
			return this.addReview(review);
		} else {
			return false;
		}

	}

	public void addSaved(Site site, Integer rating) throws ReadException {
		if (!this.hasSaved(site)) {
			this.addReview(site, null, null, rating, UserPrivacyType.PUBLIC,
					null, null);
		}
	}

	public boolean discoverService(String name, String address,
			String locality, String countryCode, String phone, String url,
			String title, String content, Integer rating,
			UserPrivacyType privacy, List<String> tags, List<String> lists)
			throws ReadException {
		Site site = new Site(oos4Java, name, address, locality, countryCode,
				phone, url);
		return this.addReview(site, title, content, rating, privacy, tags,
				lists);
	}

	public void loadAgenda() throws ReadException {
		DynamicList<ReviewEntry> reviewList;
		reviewList = ((DynamicList<ReviewEntry>) getUserSiteDao().read());
		reviewList.loadAll();
		reviews = new ReviewList(oos4Java, reviewList);

		DynamicList<ContactEntry> contactList;
		contactList = ((DynamicList<ContactEntry>) getContactDao().read());
		contactList.loadAll();
		contacts = new ContactList(oos4Java, contactList);
	}

	public void setReviews(ReviewList reviews) {
		this.reviews = reviews;
	}

	public void setContacts(ContactList contacts) {
		this.contacts = contacts;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof User) {
			User other = (User) obj;
			return other.entry.getSlug().equals(entry.getSlug());
		}
		return false;
	}

	/**
	 * @return true if the user is the one that is logged.
	 */
	public boolean isMe() {
		try {
			return getOos4Java().isAuthorized(this);
		} catch (ReadException e) {
			e.printStackTrace();
			return false;
		}
	}
}
